﻿using DeHeng_Mes.Dao;
using DeHeng_Mes.Dao.PlcDao;
using DeHeng_Mes.Mapper;
using DeHeng_Mes.Model;
using DeHeng_Mes.Model.ConfigModel;
using DeHeng_Mes.Model.Singleton;
using DeHeng_Mes.Model.Table;
using DeHeng_Mes.Utils;
using Google.Protobuf.Collections;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MappingTable = DeHeng_Mes.Utils.MappingTable;
using S7.Net;

namespace DeHeng_Mes.Thread
{
    // 定义一个委托，代表获取错误类型和错误信息的方法
    public delegate (string errorType, string errorMessage) GetErrorMessageDelegate(int byteIndex, int bitIndex);

    public class ErrorGather
    {
        PlcDao plcDao = new PlcDao();

        List<ErrorCache> lists = ErrorList.Lists;

        DeviceDao deviceDao = new DeviceDao();

        ErrorDao errorDao = new ErrorDao();



        public async Task PlcErrorGather()
        {
            Tools.ProduceLogText("故障采集现场初始化成功！");

            while (true)
            {
                List<Model.PlcModel.PlcConfigModel> plcContainer = PlcConnectionManager.plcContainer;


                if (plcContainer == null)
                    continue;
                if (!(plcContainer.Count > 0))
                    continue;

                foreach (var plcConfig in plcContainer)
                {
                    try
                    {
                        // 未连接，跳过
                        if (!plcConfig.isConnected)
                            continue;

                        string plcName = plcConfig.name;

                        // 获取Plc数据实例与实例类型
                        Dictionary<object, Type> obj = MappingTable.PlcMapping(plcConfig.plc, plcName);

                        // 获取故障数据
                        Dictionary<string, object> errorData = plcDao.GetFieldValues(obj, "ErrorStatus");

                        // 解析故障
                        foreach (var errorType in errorData)
                        {
                            Dictionary<int, DeviceFault> deviceFaults = new Dictionary<int, DeviceFault>();

                            string typeName = ErrorMappingTable.GetTypeName(errorType.Key);

                            if (string.IsNullOrEmpty(typeName))
                                continue;

                            if (errorType.Key.Contains("Independent") && (bool)errorType.Value)
                            {
                                // 判断故障是否已存在缓存中，存在则跳过
                                if (IsExist(true, typeName))
                                    continue;

                                // 截取索引
                                string index = errorType.Key.Replace("Independent", "");

                                deviceFaults.Add(int.Parse(index + "8000"), IndependentDataAssembly(new DeviceFault(), typeName, plcConfig));
                            }
                            else
                            {
                                if (errorType.Key.Equals("Robot"))
                                    continue;

                                if (errorData[errorType.Key] is bool[] boolArray)
                                {
                                    for (int i = 0; i < boolArray.Length; i++)
                                    {
                                        bool currentValue = boolArray[i];

                                        // 如果为真，则触发报警
                                        if (currentValue)
                                        {
                                            // 获取详细报错信息
                                            string message = ErrorMappingTable.GetMessage(plcName, errorType.Key, i);

                                            if (string.IsNullOrEmpty(message))
                                                continue;

                                            // 判断故障是否已存在缓存中，存在则跳过
                                            if (IsExist(false, message))
                                                continue;

                                            // 组装数据
                                            deviceFaults.Add(i, DataAssembly(new DeviceFault(), typeName, message, plcConfig));
                                        }
                                    }
                                }
                            }

                            // 判断DeviceFault是否存在故障数据，如果已经实例化进行下一步操作
                            if (deviceFaults.Count > 0)
                            {
                                foreach (var deviceFault in deviceFaults)
                                {
                                    // 存入数据库
                                    deviceDao.ErrorDataInit(deviceFault.Value);

                                    // 初始化故障缓存类
                                    ErrorCache errorCache = new ErrorCache()
                                    {
                                        deviceFault = deviceFault.Value,
                                        Index = deviceFault.Key,
                                        PlcName = plcName,
                                        PlcIn = plcConfig.plc,
                                    };

                                    // 存入缓存列表
                                    lists.Add(errorCache);
                                }
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        Tools.ProduceLogTextError($"故障采集失败，原因是：{e.Message}");
                    }
                }

                await Task.Delay(1000);
            }
        }

        /// <summary>
        /// 采集故障结束状态与时间
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task PlcErrorGatherEndTime()
        {
            Tools.ProduceLogText("故障结束状态检测线程启动成功！");
            while (true)
            {
                try
                {
                    // 初始化移除项
                    List<ErrorCache> errorCaches = new List<ErrorCache>();

                    if (lists != null && lists.Count > 0)
                    {
                        // 存在故障，检测故障状态
                        foreach (var errorMessage in lists)
                        {
                            // 取出故障索引
                            string index = errorMessage.Index.ToString();

                            // 取出plcName
                            string plcName = errorMessage.PlcName;

                            // 取出Plc实例
                            S7.Net.Plc plc = errorMessage.PlcIn;

                            // 取出故障数据
                            DeviceFault deviceFault = errorMessage.deviceFault;

                            // 获取Plc数据实例与实例类型
                            Dictionary<object, Type> obj = MappingTable.PlcMapping(plc, plcName);

                            // 判断索引
                            if (index.Length > 3)
                            {
                                // 提取数据
                                string indexResult = index.Replace("8000", "");

                                // 获取故障数据
                                bool status = plcDao.GetBoolFieldValue(obj, "ErrorStatus", $"Independent{int.Parse(indexResult)}");

                                if (!status)
                                {
                                    // 故障结束，处理数据
                                    deviceDao.UpDateEndErrorTime(deviceFault);

                                    errorCaches.Add(errorMessage);
                                }
                            }
                            else
                            {
                                // 处理数组数据
                                Dictionary<string, object> dictionary = plcDao.GetFieldValues(obj, "ErrorStatus");

                                foreach (var errorType in dictionary)
                                {
                                    string key = ErrorMappingTable.GetTypeKey(deviceFault.FaultType);

                                    if (!string.IsNullOrEmpty(key))

                                        if (errorType.Key.Equals(key))
                                        {
                                            if (dictionary[errorType.Key] is bool[] boolArray)
                                            {
                                                bool isOk = boolArray[int.Parse(index)];

                                                if (!isOk)
                                                {
                                                    // 故障结束，处理数据
                                                    deviceDao.UpDateEndErrorTime(deviceFault);

                                                    errorCaches.Add(errorMessage);
                                                }
                                            }
                                        }
                                }
                            }
                        }
                    }

                    if (errorCaches != null && errorCaches.Count > 0)
                    {
                        foreach (var errorCache in errorCaches)
                        {
                            lists.Remove(errorCache);

                            Tools.ProduceLogText($"成功移除了一个故障，故障信息为{errorCache.deviceFault.FaultMessage}剩余{lists.Count}个故障未处理");
                        }
                    }
                }
                catch (Exception e)
                {
                    Tools.ProduceLogTextError($"处理故障结束异常，原因是{e.Message}");
                }
                await Task.Delay(500);
            }
        }

        /// <summary>
        /// 判断报警是否存在于缓存数据中
        /// </summary>
        /// <param name="isType"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        private bool IsExist(bool isType, string message)
        {

            if (lists != null && lists.Count > 0)
            {
                foreach (var errorMessage in lists)
                {
                    if (isType)
                    {
                        if (errorMessage.deviceFault.FaultType.Equals(message))
                        {
                            return true;
                        }
                    }
                    else
                    {
                        if (errorMessage.deviceFault.FaultMessage.Equals(message))
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 组装报警数据，有详细信息
        /// </summary>
        /// <param name="deviceFault"></param>
        /// <param name="typeName"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        private DeviceFault DataAssembly(DeviceFault deviceFault, string typeName, string message, Model.PlcModel.PlcConfigModel plcConfig)
        {
            deviceFault.PlcName = plcConfig.nick;

            deviceFault.FaultType = typeName;

            deviceFault.FaultMessage = message;

            deviceFault.StopStartTime = DateTime.Now;

            return deviceFault;
        }

        /// <summary>
        /// 组装报警数据，无详细信息
        /// </summary>
        /// <param name="deviceFault"></param>
        /// <param name="typeName"></param>
        /// <returns></returns>
        private DeviceFault IndependentDataAssembly(DeviceFault deviceFault, string typeName, Model.PlcModel.PlcConfigModel plcConfig)
        {
            deviceFault.PlcName = plcConfig.name;

            deviceFault.FaultType = typeName;

            deviceFault.FaultMessage = "无具体信息";

            deviceFault.StopStartTime = DateTime.Now;

            return deviceFault;
        }

        /// <summary>
        /// 采集报警
        /// </summary>
        /// <returns></returns>
        public async Task GetError()
        {
            Tools.ProduceLogText("报警采集线程启动成功！");
            while (true)
            {
                try
                {
                    List<PlcOrderConfigModel> readPlcList = PlcOrderConfigModelList.ReadPlcList.Where(x => x.plc.IsConnected == true).ToList();

                    foreach (var item in readPlcList)
                    {
                        switch (item.PlcName)
                        {
                            case "P5":
                                HandlePlcAlarm(item.PlcName, item.plc, MappingTable.GetP5ErrorMessage);
                                break;
                            case "P4":
                                HandlePlcAlarm(item.PlcName, item.plc, MappingTable.GetP4ErrorMessage);
                                break;
                            case "P3":
                                HandlePlcAlarm(item.PlcName, item.plc, MappingTable.GetP3ErrorMessage);
                                break;

                            default:
                                break;

                        }

                    }

                }
                catch (Exception ex)
                {
                    Tools.ProduceLogText($"采集报警信息失败，原因为{ex.Message}");
                }
                await Task.Delay(1000);
            }
        }
        
        

        /// <summary>
        /// 判断报警是否存在于缓存数据中
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        private bool IsExist(string message)
        {
            return ErrorDic.GetAllAlarms().Values.Contains(message);
        }


        private void HandlePlcAlarm(string plcName, Plc plc, GetErrorMessageDelegate getErrorMessage)
        {
            //读取报警信息
            byte[] bytes = plc.ReadBytes(DataType.DataBlock, 5110, 0, 204);

            for (int i = 0; i < bytes.Length; i++)
            {
                //处理int报警
                if (i >= 2 && i <= 102)
                {
                    if (i % 2 == 0)
                    {
                        //拿到报警状态
                        int status = S7.Net.Types.Int.FromByteArray(bytes.Skip(i).Take(2).ToArray());
                        //那拿到报警类型和报警信息
                        (string errorType, string errorMessage) =getErrorMessage(i, 0);

                        // 存在报警,先获取对应的报警文本
                        if (string.IsNullOrEmpty(errorMessage))
                            continue;

                        // 1.到全局缓存中查找有报警信息
                        bool isExit = IsExist(errorMessage);

                        if (status == 1 && !isExit)
                        {
                            // 2.缓存有报警信息就跳过，没有报警信息存储数据库记录开始时间并且全局缓存报警，记录缓存时候带上数据库主键
                            DeviceFault deviceFault = new DeviceFault()
                            {

                                PlcName = plcName,

                                FaultType = errorType,

                                FaultMessage = errorMessage,

                                StopStartTime = DateTime.Now,
                            };
                            //插入数据库返回id
                            int id = errorDao.insertError(deviceFault);
                            //将报警存储在缓存中
                            ErrorDic.AddAlarm(id, errorMessage);

                            Tools.ProduceLogText($"添加了一条报警信息到缓存中，报警信息为{deviceFault.FaultMessage}");
                        }
                        else if (status == 0 && isExit)
                        {
                            // 1.到全局缓存找有没有当前报警缓存，有就根据主键更新数据库中结束时间并且清除当前一条缓存，没有就跳过
                            int id = ErrorDic.GetAlarmIdByMessage(errorMessage);

                            if (id != 0)
                            {
                                // 更新数据库结束时间
                                errorDao.updateError(DateTime.Now, id);

                                // 从缓存移除
                                ErrorDic.RemoveAlarm(id);

                                Tools.ProduceLogText($"从缓存中移除了一条报警信息，报警缓存id为{id}，报警信息为{errorMessage}");
                            }
                        }
                    }
                }
                else
                {
                    //处理bool类型报警
                    for (int j = 0; j < 8; j++)
                    {
                        bool isError = S7.Net.Types.Bit.FromByte(bytes[i], (byte)j);

                        (string errorType, string errorMessage) = getErrorMessage(i, j);

                        bool isExit = IsExist(errorMessage);

                        if (isError && !isExit)
                        {
                            // 存在报警,先获取对应的报警文本
                            if (string.IsNullOrEmpty(errorMessage))
                                continue;

                            // 2.缓存有报警信息就跳过，没有报警信息存储数据库记录开始时间并且全局缓存报警，记录缓存时候带上数据库主键
                            DeviceFault deviceFault = new DeviceFault()
                            {

                                PlcName = plcName,

                                FaultType = errorType,

                                FaultMessage = errorMessage,

                                StopStartTime = DateTime.Now,
                            };

                            int id = errorDao.insertError(deviceFault);

                            ErrorDic.AddAlarm(id, errorMessage);

                            Tools.ProduceLogText($"添加了一条报警信息到缓存中，报警信息为{deviceFault.FaultMessage}");
                        }
                        else if (!isError && isExit)
                        {
                            // 1.到全局缓存找有没有当前报警缓存，有就根据主键更新数据库中结束时间并且清除当前一条缓存，没有就跳过
                            int id = ErrorDic.GetAlarmIdByMessage(errorMessage);

                            if (id != 0)
                            {
                                // 更新数据库结束时间
                                errorDao.updateError(DateTime.Now, id);

                                // 从缓存移除
                                ErrorDic.RemoveAlarm(id);

                                Tools.ProduceLogText($"从缓存中移除了一条报警信息，报警缓存id为{id}，报警信息为{errorMessage}");
                            }
                        }
                    }
                }
            }

        }
    }
}
